/*
 *  Copyright (C) 2011 in-somnia
 * 
 *  This file is part of JAAD.
 * 
 *  JAAD is free software; you can redistribute it and/or modify it 
 *  under the terms of the GNU Lesser General Public License as 
 *  published by the Free Software Foundation; either version 3 of the 
 *  License, or (at your option) any later version.
 *
 *  JAAD is distributed in the hope that it will be useful, but WITHOUT 
 *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
 *  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General 
 *  Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library.
 *  If not, see <http://www.gnu.org/licenses/>.
 */
package net.sourceforge.jaad.aac.filterbank;

import net.sourceforge.jaad.aac.AACException;

class MDCT implements MDCTTables {

	private final int N, N2, N4, N8;
	private final float[][] sincos;
	private final FFT fft;
	private final float[][] buf;
	private final float[] tmp;

	MDCT(int length) throws AACException {
		N = length;
		N2 = length>>1;
		N4 = length>>2;
		N8 = length>>3;
		switch(length) {
			case 2048:
				sincos = MDCT_TABLE_2048;
				break;
			case 256:
				sincos = MDCT_TABLE_128;
				break;
			case 1920:
				sincos = MDCT_TABLE_1920;
				break;
			case 240:
				sincos = MDCT_TABLE_240;
			default:
				throw new AACException("unsupported MDCT length: "+length);
		}
		fft = new FFT(N4);
		buf = new float[N4][2];
		tmp = new float[2];
	}

	void process(float[] in, int inOff, float[] out, int outOff) {
		int k;

		//pre-IFFT complex multiplication
		for(k = 0; k<N4; k++) {
			buf[k][1] = (in[inOff+2*k]*sincos[k][0])+(in[inOff+N2-1-2*k]*sincos[k][1]);
			buf[k][0] = (in[inOff+N2-1-2*k]*sincos[k][0])-(in[inOff+2*k]*sincos[k][1]);
		}

		//complex IFFT, non-scaling
		fft.process(buf, false);

		//post-IFFT complex multiplication
		for(k = 0; k<N4; k++) {
			tmp[0] = buf[k][0];
			tmp[1] = buf[k][1];
			buf[k][1] = (tmp[1]*sincos[k][0])+(tmp[0]*sincos[k][1]);
			buf[k][0] = (tmp[0]*sincos[k][0])-(tmp[1]*sincos[k][1]);
		}

		//reordering
		for(k = 0; k<N8; k += 2) {
			out[outOff+2*k] = buf[N8+k][1];
			out[outOff+2+2*k] = buf[N8+1+k][1];

			out[outOff+1+2*k] = -buf[N8-1-k][0];
			out[outOff+3+2*k] = -buf[N8-2-k][0];

			out[outOff+N4+2*k] = buf[k][0];
			out[outOff+N4+2+2*k] = buf[1+k][0];

			out[outOff+N4+1+2*k] = -buf[N4-1-k][1];
			out[outOff+N4+3+2*k] = -buf[N4-2-k][1];

			out[outOff+N2+2*k] = buf[N8+k][0];
			out[outOff+N2+2+2*k] = buf[N8+1+k][0];

			out[outOff+N2+1+2*k] = -buf[N8-1-k][1];
			out[outOff+N2+3+2*k] = -buf[N8-2-k][1];

			out[outOff+N2+N4+2*k] = -buf[k][1];
			out[outOff+N2+N4+2+2*k] = -buf[1+k][1];

			out[outOff+N2+N4+1+2*k] = buf[N4-1-k][0];
			out[outOff+N2+N4+3+2*k] = buf[N4-2-k][0];
		}
	}

	void processForward(float[] in, float[] out) {
		int n, k;
		//pre-FFT complex multiplication
		for(k = 0; k<N8; k++) {
			n = k<<1;
			tmp[0] = in[N-N4-1-n]+in[N-N4+n];
			tmp[1] = in[N4+n]-in[N4-1-n];

			buf[k][0] = (tmp[0]*sincos[k][0])+(tmp[1]*sincos[k][1]);
			buf[k][1] = (tmp[1]*sincos[k][0])-(tmp[0]*sincos[k][1]);

			buf[k][0] *= N;
			buf[k][1] *= N;

			tmp[0] = in[N2-1-n]-in[n];
			tmp[1] = in[N2+n]+in[N-1-n];

			buf[k+N8][0] = (tmp[0]*sincos[k+N8][0])+(tmp[1]*sincos[k+N8][1]);
			buf[k+N8][1] = (tmp[1]*sincos[k+N8][0])-(tmp[0]*sincos[k+N8][1]);

			buf[k+N8][0] *= N;
			buf[k+N8][1] *= N;
		}

		//complex FFT, non-scaling
		fft.process(buf, true);

		//post-FFT complex multiplication
		for(k = 0; k<N4; k++) {
			n = k<<1;

			tmp[0] = (buf[k][0]*sincos[k][0])+(buf[k][1]*sincos[k][1]);
			tmp[1] = (buf[k][1]*sincos[k][0])-(buf[k][0]*sincos[k][1]);

			out[n] = -tmp[0];
			out[N2-1-n] = tmp[1];
			out[N2+n] = -tmp[1];
			out[N-1-n] = tmp[0];
		}
	}
}
